﻿using System;
using System.Text;
using Real = System.Double;

namespace LinearAlgebra.Matrices
{
	/// <summary>
	/// A general purpose Row-Major-Order Matrix for use in linear algebra applications
	/// </summary>
	public partial class Matrix : RealMatrix
	{
		/// <summary>
		/// Gets the number of Rows in the Matrix
		/// </summary>
		public int RowCount { get { return this.Length; } }

		/// <summary>
		/// Gets the number of Columns in the Matrix
		/// </summary>
		public int ColumnCount 
		{
			get {
				if( this.RowCount != 0 )
					return this[0].Length;
				return 0;
			}
		}		

		public int M { 
			get {
				return RowCount;
			}
		}

		public int N {
			get {
				return ColumnCount;
			}
		}

		/// <summary>
		/// Create an m row by n column matrix where every element is 0
		/// </summary>
		/// <param name="m">rows</param>
		/// <param name="n">columns</param>
		public Matrix( int m, int n ) : base( m, n ) {}
		
		/// <summary>
		/// Constructs a square matrix of m height and width
		/// </summary>		
		public Matrix( int m ) : this( m, m ) {}

		/// <summary>
		/// Writes the A to console in a very nice and readable format, showing 5 places beyond the Real
		/// </summary>		
		public new string ToString() { return this.ToString( "f5" ); }

		/// <summary>
		/// Writes out the A using whatever format string you desire
		/// </summary>		
		/// <param name="formatString"> The format string given to the ToString method See ToString( string formatString ) for more info </param>
		public string ToString( string formatString )
		{
			StringBuilder sb = new StringBuilder();
			int[] colMaxes = new int[this.ColumnCount]; //number of columns in the first row
			for( int j = 0; j < this.ColumnCount; j++ ) //for each column
			{
				int columnWidth = 0; //the amount of padding to apply
				for( int i = 0; i < this.RowCount; i++ )
				{
					int curValLen = this[i][j].ToString( formatString ).Length;
					if( curValLen > columnWidth )
						columnWidth = curValLen;					
				}
				if( j != this.ColumnCount-1 )	//if j is not the last element
					columnWidth += 2;			//the space and a comma				
				colMaxes[j] = columnWidth; //the current columns max width
			}
			//now we have the col maxes
			sb.AppendLine( "" );
			for( int i = 0; i < this.RowCount; i++ )
			{	
				sb.Append( " [ " );
				for( int j = 0; j < this.ColumnCount; j++ )
				{				
					string value = this[i][j].ToString( formatString );					
					if( j != this.ColumnCount-1 ) //if j is not the last element
						value += ", ";

					sb.Append( value.PadLeft( colMaxes[j], ' ' ) );
				}
				sb.Append( " ] " + Environment.NewLine );
			}
			return sb.ToString();
		}

		/// <summary>
		/// Returns a string Literal Compatible with Mathematica Matrix String Literal format
		/// This is very useful when you need to pass a matrix to mathematica
		/// </summary>		
		/// <returns>Mathematica Compatible Matrix String Literal</returns>
		public string ToMathematicaLiteral()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append( "{" );
			for( int i = 0; i <this.RowCount; i++ )
			{
				sb.Append( "{" );						
				for( int j = 0; j < this[i].Length; j++ )
				{				
					sb.Append( this[i][j] );
					if( j ==this.RowCount-1 )
						continue;
					sb.Append( "," ); //otherwise
				}
				sb.Append( "}" );
				if( i ==this.RowCount-1 )
					continue;				
				sb.Append( ", " );
			}
			sb.Append( "}" );
			return sb.ToString();
		}	
		
		/// <summary>
		/// Exports the matrix into a standard format
		/// </summary>		
		public string ToMatrixLiteral( MatrixLiteralFormat format )
		{
			switch( format )
			{				
				case MatrixLiteralFormat.Mathematica:
					return this.ToMathematicaLiteral();				
				default:
					throw new Exception( format + " style matrix literal is not yet implemented." );
			}
		}				
	}
}